tree grammar ANMLTree;

// must be in the order grammar, options, tokens, scopes, actions, rules

options {
    tokenVocab=ANML;
    //backtrack=true;
    //memoize=true;
    ASTLabelType=ANMLToken; // type of $stat.tree ref etc...
    TokenLabelType=ANMLToken;
    //output=AST;
    //rewrite=true;
}

scope S {
	Scope d;  
	// capital Scope is ANML-defined --- lowercase is ANTLR-defined.
}

scope A {
	Unit d; // The difference between Scope and Unit is small: Units have names.
}

scope I {
	Interval i;
}

scope O {
	ObjectType d; // are we within the definition of an object (type)?  This may include field references in expressions.
}

/*
scope V { 
	VectorType d;
}

scope D {
	Decomposition d;
}
*/

@treeparser::header {
package gov.nasa.anml.parsing;
import gov.nasa.anml.lifted.*;
import gov.nasa.anml.utility.*;
}


@treeparser::members {
/*	// ^(Boolean)
	public static CommonTree booleanTree = new CommonTree(new CommonTree(new CommonToken(Boolean,"Boolean")));
	// ^(Delta) // intended to be ^(Delta fluent_ref) by .setChild(0,fluent_ref.tree)
	public static CommonTree deltaTree = new CommonTree(new CommonToken(Delta,"^"));
	public int LA(int i) {
		return input.LA(i);
	}
	public boolean LA(int i,int t) {
		return input.LA(i)==t;
	}
*/
	public static int size(CommonTree t) {
		if (t.isNil()) {
			return t.getChildCount();
		}
		return 1;
	}
	
	public String text(ANMLToken t) {
		if (t.startIndex <0 || t.stopIndex <0)
			return "null";
		return input.getTokenStream().toString(t.startIndex,t.stopIndex);
			
	}
	
	Domain domain ;//= new Domain();
}



 
model[Domain d]
scope I,A,S;
@init { domain=d; $S::d = d; $A::d = d; $I::i=d;}
	: ^(Block
			types
			constants
			fluents
			actions
			stmts
	)
;



term_arg_decl_list[Term t]
	: (^(Parameter id type_ref 
			{t.parameters.put(new Parameter($id.text,$type_ref.d));}
	  ))*
;

scope_arg_decl_list[Scope d]
	: (^(Parameter id type_ref 
			{d.addParameter(new Parameter($id.text,$type_ref.d));}
	  ))*
;

/* Declaration Helpers */
builtinType
returns [TypeCode typeCode]
	: 
	( t=Boolean {$typeCode=TypeCode.Boolean;}
	| t=Integer {$typeCode=TypeCode.Integer;}
	| t=Float {$typeCode=TypeCode.Float;}
	| t=Symbol {$typeCode=TypeCode.Symbol;}
	| t=String {$typeCode=TypeCode.String;}
	| t=Object {$typeCode=TypeCode.Object;}
	) {
		//System.out.println("Builtin: " + text($t));
	}
;

id returns [SimpleString text]
    : ID {$text=$ID.getSimpleText();}
    | This {$text=$A::d.name();} 
;

type_spec returns [Type d]
@init { Constraint c = null; }
    : ^(Vector {$d=new VectorType();} 
    	(^(Parameters term_arg_decl_list[(VectorType)$d]) | Parameters)
    	)
	| ^(TypeRef builtinType {$d=domain.getType($builtinType.typeCode);} 
	      (set {$d=$d.constrain($set.constraint);})?
	   )
	| ^(TypeRef id {$d=$S::d.resolveType($id.text);} 
          (set {
          	$d=$d.constrain($set.constraint);
          	if ($d instanceof ExtensibleType) {
	          	Enumeration<? extends Identifier<?,?>> values = (Enumeration<? extends Identifier<?,?>>) $set.constraint.values();
	          	for (Identifier i : values) {
	          		$S::d.addSymbol(i);
	          	}
	          }
          })?
	   )
	| enumeration {
			c = $enumeration.constraint;
			switch ($enumeration.typeCode) {
			case Object:
	          	Enumeration<ObjectLiteral> objectValues = (Enumeration<ObjectLiteral>) c;
				$d = new ObjectType($S::d,objectValues);
	          	for (Identifier<?,?> i : objectValues) {
	          		$S::d.addSymbol(i);
	          	}
			break;
			case Symbol:
          		Enumeration<SymbolLiteral> symbolValues = (Enumeration<SymbolLiteral>) c;
				$d = new SymbolType(symbolValues);
	          	for (Identifier<?,?> i : symbolValues) {
	          		$S::d.addSymbol(i);
	          	}			break;
			case Vector:
				$d = new VectorType(c);
			break;
			default:
				$d = new PrimitiveType($enumeration.typeCode,c);
			}
		}
;



type_decl
@init { Type d=null; ObjectType p=null; SymbolType q=null; boolean symbol = false; boolean object = false; }
	: ^(Type id {
			if ($S::d.getType($id.text) != null)
				System.err.println("Error: Type \"" + $id.text + "\" redefined.");
			//else System.err.println($id.text);
	 	}
		( Assign {
				p = new ObjectType($S::d,$id.text);
				q = new SymbolType($id.text);
			}
		| ^(Assign type_spec) {
			  d = $type_spec.d;
			  if (d.name() == null) {
				  d.name($id.text);
			  } else {
				  // aliasing.  could do something better than cloning.
				  d = d.clone();
				  d.name($id.text);
			  }
			  if (d.typeCode() == TypeCode.Object) {
			  	object = true;
				  (p=(ObjectType) d).setParent($S::d);
			  } else if (d.typeCode() == TypeCode.Symbol) {
			  	symbol = true;
			  	q = (SymbolType) d;
			  }
			}
		) 
		( LessThan
		| ^(LessThan (t=type_ref {
						if ($t.d.typeCode() == TypeCode.Object) {
							if (symbol) {
								System.err.println("Symbols and Objects are disjoint types: " + $id.text + ".");
								symbol = false;
							}
							object = true;
							p.extend((ObjectType)$t.d);
						}
						else if ($t.d.typeCode() == TypeCode.Symbol) {
							if (object) {
								System.err.println("Symbols and Objects are disjoint types: " + $id.text + ".");
								object = false;
							}
							symbol = true;
							q.extend((SymbolType)$t.d);
						} else {
							System.err.println("Type: " + $t.d.id() + " is not extensible.");
						}
					}
				)*
			)
		)
		( With {
	  		if (d == null) {
	  			if (symbol)
	  				d = q;
	  			else
	  				d = p;
	  		}
			$S::d.addType(d);
	  	}
		| ^(With {
				if (symbol) {
					System.err.println("Symbols and Objects are disjoint types: " + $id.text + ".");
					symbol = false;
				}
				object = true;
				d = p;
				$S::d.addType(d);
			}
			object_block[p]*)
		)
	)
;

type_refine
@init { Type d; ExtensibleType o=null; ObjectType p=null; SymbolType q=null;}
	: ^(TypeRefine r=type_ref {
			d = $r.d;
			if (d.name() == null)
				System.err.println("Error: Cannot refine anonymous types");
			// well, actually, you can, it just doesn't do any good
			if (d.typeCode() != TypeCode.Object && d.typeCode() != TypeCode.Symbol) {
				System.err.println("Error (line " + $TypeRefine.getLine() + "): " + d.name() + " is not an extensible type.");				
			}
			else {
				o = (ExtensibleType) d;
				if (o.typeCode() == TypeCode.Object) 
					p = (ObjectType) o;
				else
					q = (SymbolType) o;
			}
	 	}
	( ^(Assign type_spec) {
			d = $type_spec.d;
			if (o != null) {
				if (d.typeCode() == TypeCode.Object)
					p.set(((ObjectType)d).members());
				else if (d.typeCode() == TypeCode.Symbol)
					q.set(((SymbolType)d).members());
			}
		}	
	| ^(LessThan t=type_ref {
			d = $t.d;
			if (o != null) {
				if (d.typeCode() == TypeCode.Object)
					p.extend((ObjectType)d);
				else if (d.typeCode() == TypeCode.Symbol)
					q.extend((SymbolType)d);
			}
		})
	| ^(With object_block[p])
	|  (id {
			if (p != null) {
				ObjectLiteral l = new ObjectLiteral($id.text);
				$S::d.addSymbol(l);
				p.add(l);
			} else if (q != null) {
				SymbolLiteral m = new SymbolLiteral($id.text);
				$S::d.addSymbol(m);
				q.add(m);
			} else {
				SymbolLiteral m = new SymbolLiteral($id.text);
				$S::d.addSymbol(m);
			}
	   	}
	  )+
	)
  )
;

// need type constraint statements, and right interpretation (member-of-type
//   declarations and subtype declarations)

// need to change this to use a scope resolution mechanism that walks up the stack
// blocks are scopes, actions are scopes, objects are scopes, domains are scopes
// anything else?
// decompositions (?)...anything else?

type_ref returns [Type d]
@init {Constraint c;}
	: ^(TypeRef builtinType) { 
			d = domain.getType($builtinType.typeCode); 
			//System.out.println("TypeRef: " + d);
		}
	| ^(TypeRef builtinType set) {
			d = domain.getType($builtinType.typeCode).constrain($set.constraint);
			//System.out.println("TypeRef: " + d);
		}
	| ^(TypeRef id) { 
			d = $S::d.resolveType($id.text); 
			if (d == null) {
				d = domain.objectType;
				System.err.println("Type: " + $id.text + " is undeclared at its first reference.");
			}
			//System.out.println("TypeRef: " + $id.text + " " + d);
		}
;

                         
const_decl
@init {Expression<?,?> init=null;ConstantFunction f=null;}
	: ^(Constant id {
			if ($S::d.getSymbol($id.text) != null)
				System.err.println("Error (line " + $Constant.getLine() + "): \"" + $id.text + "\" redefined.");
		}
		type_ref (i=expression {init = $i.e;})?)
		{$S::d.addConstant(new Constant($id.text,$type_ref.d,init));}
	| ^(ConstantFunction id {
			if ($S::d.getSymbol($id.text) != null)
				System.err.println("Error (line " + $ConstantFunction.getLine() + "): \"" + $id.text + "\" redefined.");
		}
		type_ref {f=new ConstantFunction($id.text,$type_ref.d);$S::d.addConstantFunction(f);} 
	    (^(Parameters term_arg_decl_list[f]) | Parameters)
	   )
;

fluent_decl
@init {Expression<?,?> init=null;FluentFunction f=null;}
	: ^(Fluent id {
			if ($S::d.getSymbol($id.text) != null)
				System.err.println("Error (line " + $Fluent.getLine() + "): \"" + $id.text + "\" redefined.");
		}
		type_ref (i=expression {init = $i.e;})?)
		{$S::d.addFluent(new Fluent($id.text,$type_ref.d,init));}
	| ^(FluentFunction id {
			if ($S::d.getSymbol($id.text) != null)
				System.err.println("Error (line " + $FluentFunction.getLine() + "): \"" + $id.text + "\" redefined.");
		}
		type_ref {
			f=new FluentFunction($id.text,$type_ref.d);
			$S::d.addFluentFunction(f);
		}
	    (^(Parameters term_arg_decl_list[f]) | Parameters)
	  )
;




/* object definitions */

object_block[Scope d]
scope S;
@init {$S::d=d;}
  : ^(Block 
		  types
		  constants
		  fluents
		  actions
		  stmts
  )
;

/* actions */

//second argument is how you dispatch the action; give a start and an end time
//or just a start time
action_decl 
@init{Action a=null;}
: 
	^(Action id {
			a=$S::d.resolveAction($id.text);
			assert a != null;
			a.setParent($S::d); // just in case scope pointer was wrong
		}
		(^(Parameters scope_arg_decl_list[a]) | Parameters) 
		start_parameter[a]
		duration_parameter[a]?		
		action_block[a]
	)
;

start_parameter[Action a]
@init {
	Parameter<SimpleFloat> s = new Parameter<SimpleFloat>(IntervalImp.startName,Unit.floatType); 
	a.addParameter(s); 
	a.start.init = s;
}
:
;
                   
duration_parameter[Action a]
@init {
	Parameter<SimpleFloat> d = new Parameter<SimpleFloat>(IntervalImp.durationName,Unit.floatType); 
	a.addParameter(d); 
	a.duration.init = d; 
}
:
	Duration
;

action_block[Action a]
scope S, A, I;
@init {$S::d=a;$A::d=a;$I::i=a;}
: 
	types
	constants
	fluents
	actions
	stmts
	decompositions
;

types :
	^(Types (type_decl|type_refine)*)
	| Types
;
constants :
	^(Constants const_decl*) 
	| Constants
;
fluents :
	^(Fluents fluent_decl*) 
	| Fluents
;

actions :
	^(Actions action_decl*)
	| Actions
;

stmts :
	^(Stmts stmts_helper*) 
	| Stmts
;
stmts_helper : stmt {$S::d.addStatement($stmt.s);} ;

decompositions :
	^(Decompositions decomps_helper*) 
	| Decompositions
;
decomps_helper : decomp[$S::d] {$A::d.addDecomposition($decomp.s);} ;

decomp[Scope parent] returns [Decomposition s]
scope S;
	: ^(Block { $S::d=s=new Decomposition(parent,$Block.textSimple); } 
		types
		constants
		fluents
		actions
		stmts
	  )
;

block[Scope parent] returns [Block s]
scope S;
	: ^(Block {$S::d=s=new Block(parent,$Block.textSimple);} 
		types
		constants
		fluents
		actions
		stmts
	  )
;

sub_block[Block b]
scope S;
@init{$S::d=b;}
	: ^(Block 
		types
		constants
		fluents
		actions
		stmts
	  )
;

stmt returns [Statement s]
	: simple_stmt { s=$simple_stmt.s; }
	| block[$S::d] { s=$block.s; }
;

timed_stmt[Interval i] returns [Statement s]
scope I;
@init { $I::i = i;}
	: stmt {$s = $stmt.s;}
;

// theoretically allows optimizing blocks containing single statements
simple_stmt returns [Statement s]
@init { 
	ForAll forall=null; 
	Exists exists=null; 
	$s = null; 
	Expression<SimpleBoolean,?> g;
	TimedStatement ts=null;
}
	: ^(When guard=expression then=stmt) {$s=new WhenStatement((Expression<SimpleBoolean,?>) $guard.e,$then.s);}
	| ^(WhenElse guard=expression then=stmt elsel=stmt) {$s=new WhenElseStatement((Expression<SimpleBoolean,?>) $guard.e,$then.s,$elsel.s);}
	| ^(ForAllStmt {$s = forall = new ForAll($S::d,$ForAllStmt.getSimpleText());} 
	    ^(Parameters scope_arg_decl_list[forall]) 
	    ( sub_block[forall]
	    | sDo=simple_stmt {forall.addStatement($sDo.s);}
	    )
	  )
	| ^(ExistsStmt {$s = exists = new Exists($S::d,$ExistsStmt.getSimpleText());} 
	    ^(Parameters scope_arg_decl_list[exists])
	    ( sub_block[exists]  // NOT recommended
	    | sHolds=simple_stmt {exists.addStatement($sHolds.s);}
	    )
	  )
	| ^(ContainsSomeStmt interval[ts] timed_stmt[ts])
	| ^(ContainsAllStmt stmt)
	| ^(TimedStmt {$s = ts = new TimedStatement();} 
			interval[ts] timed_stmt[ts] {ts.s = $timed_stmt.s;}
		)
	| ^(Chain chain_stmt) {$s = $chain_stmt.c;}
	| expr=expression { $s = $expr.e; }
;

chain_stmt returns [Chain c]
@init {
	c = new Chain();
	Expression<?,?> l=null; 
}
    : interval[c] ref {l = $ref.e;}
	( ^(Equal r=expression) {
			c.expressions.add(new OpBinary(TypeCode.Boolean,Op.equal,l,$r.e));
		}
	| ^(NotEqual r=expression) {
			c.expressions.add(new OpBinary(TypeCode.Boolean,Op.notEqual,l,$r.e));
		}
	| ^(LessThan r=expression) {
			c.expressions.add(new OpBinary(TypeCode.Boolean,Op.lessThan,l,$r.e));
		}
	| ^(GreaterThan r=expression) {
			c.expressions.add(new OpBinary(TypeCode.Boolean,Op.greaterThan,l,$r.e));
		}
	| ^(LessThanE r=expression) {
			c.expressions.add(new OpBinary(TypeCode.Boolean,Op.lessThanE,l,$r.e));
		}
	| ^(GreaterThanE r=expression) {
			c.expressions.add(new OpBinary(TypeCode.Boolean,Op.greaterThanE,l,$r.e));
		}
	| ^(Assign r=expression) {
			if (l instanceof Constant) {
				Constant lc = (Constant) l;
				if (lc.name == IntervalImp.startName || lc.name == IntervalImp.endName || lc.name == IntervalImp.durationName) {
					if (lc.init == null)
						lc.init = $r.e;
					else
					  	System.err.println("Can only set time parameters once.");
					c.expressions.add(new Skip());
				} else {
					c.expressions.add(new Assign(l,$r.e));
					// might be nested within when statements and similar, so that one has to dynamically check whether multiple inits have happened					
					if (lc.init != null)
						System.err.println("Constant " + lc.name + " is defined twice?\n");
				}
			} else {
				c.expressions.add(new Assign(l,$r.e));
			}
		}
	| ^(Change r=expression) {
			c.expressions.add(new Change(l,$r.e));
		}
	| ^(Lend r=expression) {
			c.expressions.add(new Lend(l,$r.e));
		}
	| ^(Use r=expression) {
			c.expressions.add(new Use(l,$r.e));
		}
	| ^(Produce r=expression) {
			c.expressions.add(new Produce(l,$r.e));
		}
	| ^(Consume r=expression) {
			c.expressions.add(new Consume(l,$r.e));
		}
	| ^(SetAssign set) {
			c.expressions.add(new Undefine(l));
		}
	| ^(Within set) {
			c.expressions.add(new Skip());
		}
	| Undefine {
			c.expressions.add(new Undefine(l));
		}
	| Skip {
			c.expressions.add(new Skip());
		}
	)+
;

float_expression returns [Expression<SimpleFloat,?> e]
:
	expression {$expression.e.typeCode() == TypeCode.Float}? { $e = (Expression<SimpleFloat,?>) $expression.e; }
;

boolean_expression returns [Expression<SimpleBoolean,?> e]
:
	expression {$expression.e.typeCode() == TypeCode.Boolean}? { $e = (Expression<SimpleBoolean,?>) $expression.e; }
;

//for intervals:
//no definition means no shadowing?
//and indefinites explicitly map to undefined?
//or no definition means must choose, and inheritance has to be explicit?
interval[IntervalImp i]
	: ^(DefiniteInterval b=bra 
		( ^(TDuration d=float_expression) ^(TEnd e=float_expression) k1=ket { 
			i.setShape($b.b,$k1.k);
			i.makeDuration($d.e); 
			i.makeEnd($e.e); 
			i.inferStart(); 
			} 
		| ^(TStart s=float_expression)
	    	( ^(TDuration d=float_expression) 
	    		( ^(TEnd e=float_expression) k2=ket { 
	    				i.setShape($b.b,$k2.k);
	    				i.makeStart($s.e);
	    				i.makeDuration($d.e);
	    				i.makeEnd($e.e);
	    			}
	    		| k2=ket {
    					i.setShape($b.b,$k2.k);
    					i.makeStart($s.e);
    					i.makeDuration($d.e);
    			    	i.inferEnd();
	    			}
	    		)
		    | ^(TEnd e=float_expression) k2=ket {
				i.setShape($b.b,$k2.k);
    			i.makeStart($s.e);
    			i.makeEnd($e.e); 
    			i.inferDuration();
	    		}
	    	)
	  	)
	  )
	| ^(DefinitePoint ^(TStart s=float_expression) {
			i.setShape(0,0);
			i.makeStart($s.e);
			i.makeEnd($s.e);
			i.inferDuration(); 
			// surely the following should work?
			//i.duration = IntervalImp.constantDurationZero;
		}
	   )
	| ^(IndefiniteInterval b=bra 
		( ^(TDuration d=float_expression) k1=ket { 
			i.setShape($b.b,$k1.k); 
			i.makeDuration($d.e); 
			i.start = new Constant<SimpleFloat>(IntervalImp.startName,Unit.floatType);
			i.end = new Constant<SimpleFloat>(IntervalImp.endName,Unit.floatType);
			}
		| ^(TEnd e=float_expression) k2=ket { 
			i.setShape($b.b,$k2.k); 
			i.makeEnd($e.e); 
			i.start = new Constant<SimpleFloat>(IntervalImp.startName,Unit.floatType);
			i.duration = new Constant<SimpleFloat>(IntervalImp.durationName,Unit.floatType);
			}
		| ^(TStart s=float_expression) k3=ket { 
			i.setShape($b.b,$k3.k); 
			i.makeStart($s.e); 
			i.duration = new Constant<SimpleFloat>(IntervalImp.durationName,Unit.floatType);
			i.end = new Constant<SimpleFloat>(IntervalImp.endName,Unit.floatType);
			}
		| k4=ket { 
			i.setShape($b.b,$k4.k); 
			i.start = new Constant<SimpleFloat>(IntervalImp.startName,Unit.floatType);
			i.duration = new Constant<SimpleFloat>(IntervalImp.durationName,Unit.floatType);
			i.end = new Constant<SimpleFloat>(IntervalImp.endName,Unit.floatType);
			}
		)
	  )
	| IndefinitePoint { 
			i.setShape(0,0); 
			i.duration = IntervalImp.constantDurationZero;
			i.start = i.end = new Constant<SimpleFloat>(IntervalImp.startName,Unit.floatType);
		}
;

bra returns [Expression b] 
	: ^(TBra 
		( At {b=IntervalImp.makeBra(Time.At);} 
		| After {b=IntervalImp.makeBra(Time.After);} 
		| Before {b=IntervalImp.makeBra(Time.Before);}
		| expression {b = $expression.e;}
		)
	  )
;
ket returns [Expression k] 
	: ^(TKet 
		( At {k=IntervalImp.makeKet(Time.At);} 
		| Before {k=IntervalImp.makeKet(Time.Before);} 
		| After {k=IntervalImp.makeKet(Time.After);}
		| expression {k=$expression.e;}
		)
	  )
;

//parallel language; comparisons and assignments evaluate to left hand side.

timed_expression[Interval i] returns [Expression<?,?> e]
scope I;
@init {$I::i = i;}
	: expression {$e = $expression.e;}
;

expression returns [Expression<?,?> e]
@init {
	LabeledExpression label;
	ForAll forall=null;
	Exists exists=null;
	Ordered ordered=null;
	Unordered unordered=null;
	TimedExpression te=null;
}
	: ^(Label id f=expression) {
		if ($f.e == null || $f.e.typeCode() != TypeCode.Boolean) {
			System.err.println("Error (line " + $Label.getLine() + "): Cannot label non-boolean expressions. [label: " + $id.text + "]");
			// suppress undefined errors later.
			$e = label = new LabeledExpression($id.text,ANMLBoolean.False);
			$A::d.labels.put(label);
		} else {
			$e = label = new LabeledExpression($id.text,(Expression<SimpleBoolean,?>)$f.e);
			$A::d.labels.put(label);
		}
	}
	| ^((Equal|EqualLog) l=expression r=expression) {
			$e = new OpBinary(TypeCode.Boolean,Op.equal,$l.e,$r.e);
		}
	| ^(NotEqual l=expression r=expression) {
			$e = new OpBinary(TypeCode.Boolean,Op.notEqual,$l.e,$r.e);
		}
	| ^(LessThan l=expression r=expression) {
			$e = new OpBinary(TypeCode.Boolean,Op.lessThan,$l.e,$r.e);
		}
	| ^(GreaterThan l=expression r=expression) {
			$e = new OpBinary(TypeCode.Boolean,Op.greaterThan,$l.e,$r.e);
		}
	| ^(LessThanE l=expression r=expression) {
			$e = new OpBinary(TypeCode.Boolean,Op.lessThanE,$l.e,$r.e);
		}
	| ^(GreaterThanE l=expression r=expression) {
			$e = new OpBinary(TypeCode.Boolean,Op.greaterThanE,$l.e,$r.e);
		}
	| ^(Assign l=expression r=expression) {
			if ($l.e instanceof Constant) {
				Constant c = (Constant) $l.e;
				if (c.name == IntervalImp.startName || c.name == IntervalImp.endName || c.name == IntervalImp.durationName) {
					if (c.init == null)
						c.init = $r.e;
					else
					  	System.err.println("Can only set time parameters once.");
					$e = new Skip(); // shouldn't be needed...
				} else {
					// might be nested within when statements and similar, so that one has to dynamically check whether multiple inits have happened					
					$e = new Assign($l.e,$r.e);
					if (c.init != null)
						System.err.println("Constant " + c.name + " is defined twice?\n");
				}
			} else {
				$e = new Assign($l.e,$r.e);
			}
		}
	| ^(Undefine l=expression) {
			$e = new Undefine($l.e);
		}
	| Skip {
			$e = new Skip();
		}
	| ^(Change l=expression r=expression) {
			$e = new Change($l.e,$r.e);
		}
	| ^(Lend l=expression r=expression) {
			$e = new Lend($l.e,$r.e);
		}
	| ^(Use l=expression r=expression) {
			$e = new Use($l.e,$r.e);
		}
	| ^(Produce l=expression r=expression) {
			$e = new Produce($l.e,$r.e);
		}
	| ^(Consume l=expression r=expression) {
			$e = new Consume($l.e,$r.e);
		}
	| ^(Within expression set) {
		}
	| ^(SetAssign expression set) {
		}
	| ^(Implies l=expression r=expression) {
			$e = new OpBinary(TypeCode.Boolean,Op.implies,$l.e,$r.e);
		}
	| ^((XorLog|XorBit) l=expression r=expression) {
			$e = new OpBinary(TypeCode.Boolean,Op.xor,$l.e,$r.e);
		}
	| ^((AndLog|AndBit) l=expression r=expression) {
			$e = new OpBinary(TypeCode.Boolean,Op.and,$l.e,$r.e);
		}
	| ^((OrLog|OrBit) l=expression r=expression) {
			$e = new OpBinary(TypeCode.Boolean,Op.or,$l.e,$r.e);
		}
	| ^(Plus l=expression r=expression) {
			$e = new OpBinary($l.e.typeCode(),Op.add,$l.e,$r.e);
		}
	| ^(Minus l=expression r=expression) {
			$e = new OpBinary($l.e.typeCode(),Op.subtract,$l.e,$r.e);
		}
	| ^(Times l=expression r=expression) {
			$e = new OpBinary($l.e.typeCode(),Op.multiply,$l.e,$r.e);
		}
	| ^(Divide l=expression r=expression) {
			$e = new OpBinary($l.e.typeCode(),Op.divide,$l.e,$r.e);
		}
	| ^(ForallExpr {$e = forall = new ForAll($S::d,$ForallExpr.getSimpleText());} 
			^(Parameters scope_arg_decl_list[forall]) l=expression) {
			forall.addStatement($l.e);
		}
	| ^(ExistsExpr {$e = exists = new Exists($S::d,$ExistsExpr.getSimpleText());}
			^(Parameters scope_arg_decl_list[exists]) l=expression) {
			exists.addStatement($l.e);
		}
	| ^(ContainsSomeExpr {te = new TimedExpression(); $e = new ContainsExpression(te);}
		interval[te] tl=timed_expression[te] {
			te.e = $tl.e;
		}
	  )
	| ^(ContainsAllExpr b=boolean_expression {
			$e = new ContainsExpression(new TimeOfExpression($b.e));
		}
	  )
	| ^(TimedExpr {$e = te = new TimedExpression();} interval[te] tl=timed_expression[te] {te.e = $tl.e;})
	| ^(Unordered {$e = unordered = new Unordered();} (b=boolean_expression {unordered.add($b.e);})*)
	| ^(Ordered {$e = ordered = new Ordered();} (b=boolean_expression {ordered.add($b.e);})*)
	| ^((NotLog|NotBit) 
		( (ref)=> ref {
			$e = $ref.e;
			$ref.e.op = Op.refNot;
			}
		| l=expression {
			$e = new OpUnary(TypeCode.Boolean,Op.not,$l.e);
			}
		)
	  )
	| ref { $e = $ref.e; }
	| ^(Delta ref)
	| literal { $e = $literal.e; }
	| time_primitive { $e = $time_primitive.e; }
;

ref returns [OpUnary e]
@init {$e = new OpUnary(TypeCode.Boolean,Op.ref,null);}
:
	  identifier_ref { $e.exp = $identifier_ref.i; $e.baseType = $e.exp.typeCode(); }
	| field_ref { $e.exp = $field_ref.f; $e.baseType = $e.exp.typeCode();}
	| term_ref { $e.exp = $term_ref.t; $e.baseType = $e.exp.typeCode();}
;

identifier_ref returns [Identifier<?,?> i]
: 
	^(Ref id) { 
		$i=$S::d.resolveSymbol($id.text); 
		if ($i == null) {
			System.err.println("Undeclared identifier in " + $A::d.name() + ": " + $id.text); 
			$S::d.addSymbol(i=new SymbolLiteral($id.text));
		}
		//System.out.println(((Domain)$A::d.parent).name()); 
	}
;

// TODO	
field_ref returns [Expression f]
:
	^(Access ref identifier_ref) {
		$f = $identifier_ref.i;
	}
;

// delaying the lookup when the first parameter is a complicated reference
// is hairy unless all the binding expressions are constants
// and, even then, it is still annoying
term_ref returns [Expression t]
@init { Bind<?,?,?> b=null; LabeledExpression l; }
:
	^(Bind r=identifier_ref {
			switch ($r.i.idCode()) {
			case Action:
				ActionReference ar;
				$t = b = ar = new ActionReference((Action) $r.i);
				if ($A::d.labels.get($r.i.name()) == null) {
					l = new LabeledExpression($r.i.name(),ar);
					$A::d.labels.put(l);
				}
				break;
			case FluentFunction:
				$t = b = new FluentFunctionReference((FluentFunction)$r.i);
				break;
			case ConstantFunction:
				$t = b = new ConstantFunctionReference((ConstantFunction)$r.i);
				break;
			// just in case we (partially) realize that a 0-arity function is not a function
			case Fluent:
				$t = $r.i;
				break;
			case Constant:
				$t = $r.i;
				break;
			default:
				$t = null;
			}
		} 
		bind_arg_list[b]
	)
;

// accessing a constant or a fluent by attaching arguments will fail with NPE
bind_arg_list [Bind<?,?,?> b] 
:
	^(Arguments (expression {$b.addArgument((Expression<? extends SimpleObject<?>,?>)$expression.e);})*) 
	| Arguments
;


time_primitive returns [Expression e]
	: ^(LabelRef id 
			( Start { e = $A::d.labels.get($id.text).getStart(); }
			| End { e = $A::d.labels.get($id.text).getEnd(); }
			| Duration { e = $A::d.labels.get($id.text).getDuration(); }
			| Bra { e = $A::d.labels.get($id.text).getBra(); }
			| Ket { e = $A::d.labels.get($id.text).getKet(); }
			)
		)
	| Start { e = $I::i.getStart(); }
	| End { e = $I::i.getEnd(); } 
	| Duration { e = $I::i.getDuration(); }
	| Bra { e = $I::i.getBra(); }
	| Ket { e = $I::i.getKet(); }
;
	
set returns [Constraint constraint]
: 
	  enumeration {$constraint = $enumeration.constraint;}
	| range { $constraint = $range.constraint;}
;

enumeration returns [Enumeration constraint, TypeCode typeCode]
@init { Expression<?,?> e;}
: 
	^(Enum f=expression {
			e = $f.e;
			$typeCode = e.typeCode();
			$constraint = new Enumeration();
			$constraint.add(e.value(null));
		} 
		(n=expression {
				e = $n.e;
				$constraint.add(e.value(null));
			}
		)*
	)
;

range returns [Range constraint]
: 
	^(Range l=expression h=expression {
			$constraint = new Range<SimpleObject>((SimpleObject)$l.e.value(null),(SimpleObject)$h.e.value(null));
		}
	) 
;

literal returns [Expression e]
	: t=INT { e = ANMLInteger.make(java.lang.Integer.parseInt($t.getText())); }
	| t=FLOAT { e = ANMLFloat.make(java.lang.Float.parseFloat($t.getText())); }
	| t=STRING { e = ANMLString.make($t.getSimpleText().v); } 
	| t=True { e = ANMLBoolean.True; }
	| t=False { e = ANMLBoolean.False; }
	| t=Infinity { e = ANMLFloat.PInf; } 
	| t=ID { e = ANMLString.make($t.getSimpleText().v); }
//	| vector_literal
;
//vector_literal : '('! literal (',' literal)* ')'!;
//vector_init : arg_list;

